import {_decorator, Node} from 'cc';

const {ccclass, property} = _decorator;

/**
 * 工具类
 * 判空
 * 拷贝
 * 数组
 * 时间日期
 * 格式化
 */
export class Utils {
    /**
     * 获取性能等级
     * -Android
     * 设备性能等级，取值为：
     * -2 或 0（该设备无法运行小游戏）
     * -1（性能未知）
     * >=1（设备性能值，该值越高，设备性能越好，目前最高不到50)
     * -IOS
     * 微信不支持IO性能等级
     * iPhone 5s 及以下
     * 设定为超低端机 benchmarkLevel = 5
     * iPhone 6 ～ iPhone SE
     * 设定为超低端机 benchmarkLevel = 15
     * iPhone 7 ~ iPhone X
     * 设定为中端机 benchmarkLevel = 25
     * iPhone XS 及以上
     * 设定为高端机 benchmarkLevel = 40
     * -H5或其他
     * -1（性能未知）
     */
    /**
     * 获取当前机型性能是否为低端机
     */
    public static checkIsLowPhone(): Boolean {
        // @ts-ignore
        if (window.wx) {
            // 微信性能数值参考:https://developers.weixin.qq.com/minigame/dev/guide/performance/perf-benchmarkLevel.html

            let currentLevel: number = -1; // currentLevel = -1性能未知
            // @ts-ignore
            const sys = window.wx.getSystemInfoSync();
            const isIOS = sys.system.indexOf('iOS') >= 0;
            if (isIOS) {
                // 微信不支持IO性能等级
                const model = sys.model;
                console.log("###model", model);
                // iPhone 5s 及以下 设定为超低端机
                const ultraLowPhoneType = ['iPhone1,1', 'iPhone1,2', 'iPhone2,1', 'iPhone3,1', 'iPhone3,3', 'iPhone4,1', 'iPhone5,1', 'iPhone5,2', 'iPhone5,3', 'iPhone5,4', 'iPhone6,1', 'iPhone6,2'];
                // iPhone 6 ~ iPhone SE 设定为超低端机
                const lowPhoneType = ['iPhone6,2', 'iPhone7,1', 'iPhone7,2', 'iPhone8,1', 'iPhone8,2', 'iPhone8,4'];
                // iPhone 7 ~ iPhone X 设定为中端机
                const middlePhoneType = ['iPhone9,1', 'iPhone9,2', 'iPhone9,3', 'iPhone9,4', 'iPhone10,1', 'iPhone10,2', 'iPhone10,3', 'iPhone10,4', 'iPhone10,5', 'iPhone10,6', "iPhone XS"];
                // iPhone XS 及以上 设定为高端机
                const highPhoneType = ['iPhone11,2', 'iPhone11,4', 'iPhone11,6', 'iPhone11,8', 'iPhone12,1', 'iPhone12,3', 'iPhone12,5', 'iPhone12,8', "iPhone 13", "iPhone 14"];
                for (let i = 0; i < ultraLowPhoneType.length; i++) {
                    if (model.indexOf(ultraLowPhoneType[i]) >= 0) {
                        currentLevel = 5
                    }
                }
                for (let i = 0; i < lowPhoneType.length; i++) {
                    if (model.indexOf(lowPhoneType[i]) >= 0) {
                        currentLevel = 10
                    }
                }
                for (let i = 0; i < middlePhoneType.length; i++) {
                    if (model.indexOf(middlePhoneType[i]) >= 0) {
                        currentLevel = 20
                    }
                }
                for (let i = 0; i < highPhoneType.length; i++) {
                    if (model.indexOf(highPhoneType[i]) >= 0) {
                        currentLevel = 30
                    }
                }
            } else {
                currentLevel = sys.benchmarkLevel;
            }

            return currentLevel < 20;
        } else {
            return false;
        }
    }

    /**
     * 深度拷贝
     *
     * @param srcObj 拷贝的对象
     */
    public static clone(srcObj: any) {
        if (srcObj === null || typeof srcObj !== "object") {
            return srcObj;
        }

        let s: { [key: string]: any } = {};
        if (srcObj.constructor === Array) {
            s = [];
        }

        for (let i in srcObj) {
            if (srcObj.hasOwnProperty(i)) {
                s[i] = this.clone(srcObj[i]);
            }
        }

        return s;
    }

    /**
     * 数组去重
     * 原理：利用对象属性不能重复这一特性
     *
     * @param arr 需要去重的数组
     */
    public static distinct<T>(arr: T[]): T[] {
        if (!arr || arr.length === 0) {
            return arr;
        }
        let obj: any = {};
        for (let i of arr) {
            if (!obj[i]) {
                obj[i] = 1;
            }
        }
        return Object.keys(obj) as T[];
    }

    /**
     * 洗牌函数（打乱数组）
     *
     * @param array 需要洗牌的数组
     */
    public static shuffle<T>(array: T[]): T[] {
        if (Array.isArray(array)) {
            let newArray: T[] = array.concat();
            newArray.sort(() => {
                return 0.5 - Math.random();
            });
            return newArray;
        }
    }

    /**
     * 判断当前时间是否在有效时间内
     *
     * @param startTime 起始时间。带有时区信息
     * @param endTime 结束时间。带有时区信息
     */
    public static isNowValid(startTime: Date, endTime: Date) {
        // 结果
        let result = false;
        // 判断
        if (startTime.getDate() + '' !== 'NaN' && endTime.getDate() + '' !== 'NaN') {
            let currentTime = new Date();
            result = currentTime < endTime && currentTime > startTime;
        }
        // 返回结果
        return result;
    }

    /**
     * 返回相隔天数
     *
     * @param start 开始日期
     * @param end 结束日期
     */
    public static getDeltaDays(start: Date, end: Date) {
        let startYear: number = start.getFullYear();
        let startMonth: number = start.getMonth() + 1;
        let startDate: number = start.getDate();
        let endYear: number = end.getFullYear();
        let endMonth: number = end.getMonth() + 1;
        let endDate: number = end.getDate();

        let startTime = new Date(startYear + '/' + startMonth + '/' + startDate + ' GMT+0800').getTime();
        let endTime = new Date(endYear + '/' + endMonth + '/' + endDate + ' GMT+0800').getTime();

        let deltaTime = endTime - startTime;
        return Math.floor(deltaTime / (24 * 60 * 60 * 1000));
    }

    /**
     * 格式化钱数
     * 超过10000 转换位 10K
     * 10000K 转换为 10M
     *
     * @param money 需要被格式化的数值
     */
    public static formatMoney(money: number) {
        let units: string[] = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'B', 'N', 'D'];

        let strValue: string = '';
        for (let index: number = 0; index < units.length; index++) {
            if (money >= 10000) {
                money /= 1000;
            } else {
                strValue = Math.floor(money) + units[index];
                break;
            }
        }

        if (strValue === '') {
            strValue = Math.floor(money) + 'U'; //超过最大值就加个U
        }

        return strValue;
    }

    /**
     * 格式化数值
     *
     * @param value 需要被格式化的数值
     */
    public static formatValue(value: number): string {
        let units: string[] = [];
        let strValue: string = '';
        for (let i = 0; i < 26; i++) {
            units.push(String.fromCharCode(97 + i));
        }

        for (let idx: number = 0; idx < units.length; idx++) {
            if (value >= 10000) {
                value /= 1000;
            } else {
                strValue = Math.floor(value) + units[idx];
                break;
            }
        }

        return strValue;
    }

    /**
     * utf-8 加密
     *
     * @param input 需要加密的字符串
     */
    public static utf8Encode(input: string): string {
        input = input.replace(/\r\n/g, "\n");
        let utfText: string = "";
        for (let n: number = 0; n < input.length; n++) {
            let c: number = input.charCodeAt(n);
            if (c < 128) {
                utfText += String.fromCharCode(c);
            } else if ((c > 127) && (c < 2048)) {
                utfText += String.fromCharCode((c >> 6) | 192);
                utfText += String.fromCharCode((c & 63) | 128);
            } else {
                utfText += String.fromCharCode((c >> 12) | 224);
                utfText += String.fromCharCode(((c >> 6) & 63) | 128);
                utfText += String.fromCharCode((c & 63) | 128);
            }

        }
        return utfText;
    }

    /**
     * utf-8 解密
     *
     * @param utfText utf-8加密字符串
     */
    public static utf8Decode(utfText: string): string {
        let string = "";
        let i: number = 0;
        let c: number = 0;
        // let c1: number = 0;
        let c2: number = 0;
        let c3: number = 0;
        while (i < utfText.length) {
            c = utfText.charCodeAt(i);
            if (c < 128) {
                string += String.fromCharCode(c);
                i++;
            } else if ((c > 191) && (c < 224)) {
                c2 = utfText.charCodeAt(i + 1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            } else {
                c2 = utfText.charCodeAt(i + 1);
                c3 = utfText.charCodeAt(i + 2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }
        }
        return string;
    }

    /**
     * base64 加密
     *
     * @param input 需要加密的数据
     */
    public static base64encode(input: string) {
        let keyStr: string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
        let output: string = "", chr1: number, chr2: number, chr3: number, enc1: number, enc2: number, enc3: number,
            enc4: number, i: number = 0;
        input = this.utf8Encode(input);
        while (i < input.length) {
            chr1 = input.charCodeAt(i++);
            chr2 = input.charCodeAt(i++);
            chr3 = input.charCodeAt(i++);
            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;
            if (isNaN(chr2)) {
                enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
                enc4 = 64;
            }
            output = output +
                keyStr.charAt(enc1) + keyStr.charAt(enc2) +
                keyStr.charAt(enc3) + keyStr.charAt(enc4);
        }
        return output;
    }

    /**
     * base64 解密
     *
     * @param input 需要解密的数据
     */
    public static base64Decode(input: string) {
        let keyStr: string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
        let output: string = "";
        let chr1: number;
        let chr2: number;
        let chr3: number;
        let enc1: number;
        let enc2: number;
        let enc3: number;
        let enc4: number;
        let i: number = 0;
        input = input.replace(/[^A-Za-z0-9+\/=]/g, "");
        while (i < input.length) {
            enc1 = keyStr.indexOf(input.charAt(i++));
            enc2 = keyStr.indexOf(input.charAt(i++));
            enc3 = keyStr.indexOf(input.charAt(i++));
            enc4 = keyStr.indexOf(input.charAt(i++));
            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;
            output = output + String.fromCharCode(chr1);
            if (enc3 != 64) {
                output = output + String.fromCharCode(chr2);
            }
            if (enc4 != 64) {
                output = output + String.fromCharCode(chr3);
            }
        }
        output = this.utf8Decode(output);
        return output;
    }

    /**
     * 数据加密
     *
     * @param input 需要加密的数据
     */
    public static encrypt(input: string) {
        let b64Data = this.base64encode(input);

        let n: number = 6;
        if (b64Data.length % 2 === 0) {
            n = 7;
        }

        let encodeData: string = '';

        for (let idx = 0; idx < (b64Data.length - n + 1) / 2; idx++) {
            encodeData += b64Data[2 * idx + 1];
            encodeData += b64Data[2 * idx];
        }

        encodeData += b64Data.slice(b64Data.length - n + 1);

        return encodeData;
    }

    /**
     * 数据解密
     *
     * @param b64Data 需要解密的数据
     */
    public static decrypt(b64Data: string) {
        let n: number = 6;
        if (b64Data.length % 2 === 0) {
            n = 7;
        }

        let decodeData = '';
        for (let idx = 0; idx < b64Data.length - n; idx += 2) {
            decodeData += b64Data[idx + 1];
            decodeData += b64Data[idx];
        }

        decodeData += b64Data.slice(b64Data.length - n + 1);

        decodeData = this.base64Decode(decodeData);

        return decodeData;
    }

    /**
     * 获取两个节点的xz坐标的弧度
     *
     * @param nodeA 第一个节点
     * @param nodeB 第二个节点
     * @param isLocal 是否为本地坐标，反之为世界坐标
     */
    public static getTwoNodeXZRadius(nodeA: Node, nodeB: Node, isLocal: boolean = false) {
        const aX = isLocal ? nodeA.position.x : nodeA.worldPosition.x;
        const aZ = isLocal ? nodeA.position.z : nodeA.worldPosition.z;
        const bX = isLocal ? nodeB.position.x : nodeB.worldPosition.x;
        const bZ = isLocal ? nodeB.position.z : nodeB.worldPosition.z;
        return this.getTwoPositionXZRadius(aX, bX, aZ, bZ);
    }

    /**
     * 获取两个坐标zx分量的弧度
     *
     * @param aX 第一个坐标x
     * @param aZ 第一个坐标z
     * @param bX 第二个坐标x
     * @param bZ 第二个坐标z
     */
    public static getTwoPositionXZRadius(aX: number, aZ: number, bX: number, bZ: number) {
        return Math.atan2(aX - bX, aZ - bZ);
    }

    /**
     * 获取两个节点坐标在xz轴的距离
     *
     * @param nodeA 第一个节点
     * @param nodeB 第二个节点
     * @param isLocal 是否为本地坐标，反之为世界坐标
     */
    public static getTwoNodeXZLength(nodeA: Node, nodeB: Node, isLocal: boolean = false) {
        const aX = isLocal ? nodeA.position.x : nodeA.worldPosition.x;
        const aZ = isLocal ? nodeA.position.z : nodeA.worldPosition.z;
        const bX = isLocal ? nodeB.position.x : nodeB.worldPosition.x;
        const bZ = isLocal ? nodeB.position.z : nodeB.worldPosition.z;
        return this.getTwoPositionXZLength(aX, aZ, bX, bZ);
    }

    /**
     * 获取两个坐标在xz轴的距离
     *
     * @param aX 第一个坐标x
     * @param aZ 第一个坐标z
     * @param bX 第二个坐标x
     * @param bZ 第二个坐标z
     */
    public static getTwoPositionXZLength(aX: number, aZ: number, bX: number, bZ: number) {
        const x = aX - bX;
        const z = aZ - bZ;
        return Math.sqrt(x * x + z * z);
    }

    /***
     * 返回随机方向
     */
    public static getRandomDirector(): number {
        let v = Math.random();
        return v > 0.5 ? 1 : -1
    }

    /**
     * 返回精确到若干位数的数值
     *
     * @param v 数值
     * @param digit 位数
     */
    public static toFixed(v: number, digit: number = 2): number {
        return Number(v.toFixed(digit));
    }
}